﻿using ILRuntime.Mono.Cecil;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;

namespace Smart.Model.UI
{
    [ObjectSystem]
    public class ResourceComponentAwakeSystem : AwakeSystem<ResourceComponent>
    {
        public override void Awake(ResourceComponent self)
        {
            self.Awake();
        }
    }

    public class LoadedAssetBundle
    {
        public AssetBundle assetBundle;
        public int refCount;
    }

    public class ResourceComponent : Component
    {
        public const string gameVersionKey = @"game_version";
        protected ABInfo[] abInfos;
        protected Dictionary<string,ABInfo> abInfoDic;
        public string version { get; private set; }
        protected byte[] versionBytes;
        protected Dictionary<string, LoadedAssetBundle> loadedBundles = new Dictionary<string, LoadedAssetBundle>(32);

        protected Dictionary<string,string> needLoadedBundles = new Dictionary<string,string>();

        public void Awake()
        {
            Smart.Model.Logger.Log("<color=#00ff00>资源管理器启动成功</color>");
        }

        public IEnumerator LoadNeededBundles(System.Action onFailed)
        {
            Smart.Model.Logger.Log("<color=#00ff00>加载需要的资源</color>");
            bool succeed = true;
            var it = needLoadedBundles.GetEnumerator();
            while(succeed && it.MoveNext())
            {
                yield return LoadAssetBundle(it.Current.Key, it.Current.Value, null, () =>
                {
                    succeed = false;
                });
            }
            needLoadedBundles.Clear();
            if(succeed)
            {
                Smart.Model.Logger.Log("<color=#00ff00>加载需要的资源成功</color>");
            }
            else
            {
                Smart.Model.Logger.LogError("<color=#ff0000>加载需要的资源失败</color>");
                onFailed?.Invoke();
            }
        }

        public IEnumerator LoadBytes(string url,System.Action onFailed, int timeout = 15)
        {
            using (var uwr = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET))
            {
                var buffer = new DownloadHandlerBuffer();
                uwr.downloadHandler = buffer;
                uwr.timeout = timeout;
                yield return uwr.SendWebRequest();

                if (!uwr.isDone || uwr.isHttpError || uwr.isNetworkError)
                {
                    Smart.Model.Logger.LogError($"LoadBytes Failed:[url:{url}]:For {uwr.error}");
                    onFailed?.Invoke();
                    yield break;
                }

                if(null == buffer.data || buffer.data.Length <= 0)
                {
                    Smart.Model.Logger.LogError($"LoadBytes Failed:[url:{url}]:For data empty");
                    onFailed?.Invoke();
                    yield break;
                }

                versionBytes = buffer.data;
                int pos = 0;
                version = buffer.data.ReadString(ref pos);
                abInfos = buffer.data.GetABInfos(pos);
                abInfoDic = abInfos.Make();
            }
        }

        public void LoadAsset<T>(string bundleName, string assetName,System.Action<T> onSucceed, System.Action onFailed) where T : UnityEngine.Object
        {
            if (null == abInfoDic || !abInfoDic.ContainsKey(bundleName))
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败,AB包信息丢失");
                onFailed?.Invoke();
                return;
            }

            var abInfo = abInfoDic[bundleName];
            if (loadedBundles.ContainsKey(bundleName))
            {
                //Logger.Log($"[LoadAssetBundle]:加载AB包成功,AB包已经存在");
                if(true)
                {
                    var asset = loadedBundles[bundleName].assetBundle.LoadAsset<T>(assetName);
                    if (null == asset)
                    {
                        Logger.LogError($"[LoadAssetBundle]:加载AB包[{bundleName}]成功,加载资源[{assetName}]失败");
                        onFailed?.Invoke();
                        return;
                    }
                    //Logger.Log($"[LoadAssetBundle]:加载AB包[{bundleName}]成功:加载资源[{assetName}]成功");
                    onSucceed?.Invoke(asset);
                }
                return;
            }

            var cachedPath = PathHelper.getAssetBundleSavePath(bundleName, false, false);
            if (!System.IO.File.Exists(cachedPath))
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败,AB包不存在:[{cachedPath}]");
                onFailed?.Invoke();
                return;
            }

            var cachedAssetBundle = AssetBundle.LoadFromFile(cachedPath, abInfo.crc);
            if (null == cachedAssetBundle)
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败,AB校验失败:[{cachedPath}]");
                System.IO.File.Delete(cachedPath);
                onFailed?.Invoke();
                return;
            }

            //Logger.Log($"[LoadAssetBundle]:从本地加载成功:{cachedPath}");
            loadedBundles.Add(bundleName, new LoadedAssetBundle
            {
                assetBundle = cachedAssetBundle,
                refCount = 1,
            });

            if(true)
            {
                var asset = cachedAssetBundle.LoadAsset<T>(assetName);
                if (null == asset)
                {
                    Logger.LogError($"[LoadAssetBundle]:加载AB包[{bundleName}]成功,加载资源[{assetName}]失败");
                    onFailed?.Invoke();
                    return;
                }
                //Logger.Log($"[LoadAssetBundle]:加载AB包[{bundleName}]成功:加载资源[{assetName}]成功");
                onSucceed?.Invoke(asset);
            }
        }

        public void UnLoadAssetBundle(string name)
        {
            if(null != loadedBundles && loadedBundles.ContainsKey(name))
            {
                var ab = loadedBundles[name];
                if(null != ab)
                {
                    if(--ab.refCount == 0)
                    {
                        ab.assetBundle.Unload(true);
                        Logger.Log($"<color=#3dffff>[资源卸载]:[销毁AssetBundle] => {name}</color>");
                    }
                    else
                    {
                        Logger.Log($"<color=#3dffff>[资源卸载]:[销毁AssetBundle] => refCount:{ab.refCount}</color>");
                    }
                }
                loadedBundles.Remove(name);
            }
        }

        public void LoadAssetBundleFromNative(string bundleName, System.Action<AssetBundle> onSucceed, System.Action onFailed)
        {
            if (null == abInfoDic || !abInfoDic.ContainsKey(bundleName))
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败,AB包信息丢失");
                if (null != onFailed)
                    onFailed();
                return;
            }

            var abInfo = abInfoDic[bundleName];
            if (loadedBundles.TryGetValue(abInfo.bundleName,out LoadedAssetBundle loadedAssetBundle))
            {
                Logger.Log($"[LoadAssetBundle]:加载AB包成功,AB包已经存在");
                ++loadedAssetBundle.refCount;
                if (null != onSucceed)
                    onSucceed.Invoke(loadedAssetBundle.assetBundle);
                return;
            }

            var cachedPath = PathHelper.getAssetBundleSavePath(abInfo.bundleName, false, false);
            if (!System.IO.File.Exists(cachedPath))
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败,AB包不存在:{cachedPath}");
                if(null != onFailed)
                    onFailed.Invoke();
                return;
            }

            var cachedAssetBundle = AssetBundle.LoadFromFile(cachedPath, abInfo.crc);
            if (null == cachedAssetBundle)
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败:CRC校验错误:{cachedPath}");
                System.IO.File.Delete(cachedPath);
                if (null != onFailed)
                    onFailed.Invoke();
                return;
            }

            loadedBundles.Add(abInfo.bundleName, new LoadedAssetBundle
            {
                 assetBundle = cachedAssetBundle,
                 refCount = 1,
            });

            if (null != onSucceed)
                onSucceed.Invoke(cachedAssetBundle);
        }

        public IEnumerator LoadAssetBundle(string url,string name,System.Action<AssetBundle> onSucceed, System.Action onFailed,int timeout = 15)
        {
            if(null == abInfoDic || !abInfoDic.ContainsKey(name))
            {
                Logger.LogError($"[LoadAssetBundle]:加载AB包失败,AB包信息丢失");
                yield break;
            }

            var abInfo = abInfoDic[name];
            if(loadedBundles.TryGetValue(abInfo.bundleName,out LoadedAssetBundle loadedAssetBundle))
            {
                Logger.Log($"[LoadAssetBundle]:加载AB包成功,AB包已经存在");
                ++loadedAssetBundle.refCount;
                onSucceed?.Invoke(loadedAssetBundle.assetBundle);
                yield break;
            }

            var cachedPath = PathHelper.getAssetBundleSavePath(abInfo.bundleName, false, false);
            if(System.IO.File.Exists(cachedPath))
            {
                //Logger.Log($"[LoadAssetBundle]:尝试从本地加载:{cachedPath}");
                var cachedAssetBundle = AssetBundle.LoadFromFile(cachedPath, abInfo.crc);
                if (null != cachedAssetBundle)
                {
                    //Logger.Log($"[LoadAssetBundle]:从本地加载成功:{cachedPath}");
                    loadedBundles.Add(abInfo.bundleName, new LoadedAssetBundle
                    {
                        assetBundle = cachedAssetBundle,
                        refCount = 1,
                    });
                    onSucceed?.Invoke(cachedAssetBundle);
                    yield break;
                }

                Logger.LogError($"[LoadAssetBundle]:从本地加载失败:{cachedPath}");
                System.IO.File.Delete(cachedPath);
            }

            bool succeed = true;
            yield return DownloadAssetBundle(url, abInfo, () => { succeed = false; }, timeout);

            if(!succeed)
            {
                Logger.LogError($"[LoadAssetBundle]:重新下载{abInfo.bundleName}");
                onFailed?.Invoke();
                yield break;
            }

            var bundle = AssetBundle.LoadFromFile(cachedPath, abInfo.crc);
            if (null == bundle)
            {
                Logger.LogError($"[LoadAssetBundle]:重新加载[{abInfo.bundleName}]失败");
                yield break;
            }

            Logger.Log($"[LoadAssetBundle]:重新加载[{abInfo.bundleName}]成功");
            loadedBundles.Add(abInfo.bundleName, new LoadedAssetBundle
            {
                assetBundle = bundle,
                refCount = 1,
            });
            onSucceed?.Invoke(bundle);
        }

        public IEnumerator DownloadVersionAssetBundles(string url,System.Action onFailed, int timeout = 15, int retryTimes = 5)
        {
            bool succeed = true;

            yield return DownloadAssetBundles(url,abInfos,()=>
            {
                succeed = false;
            },timeout,retryTimes);

            if(!succeed)
            {
                onFailed?.Invoke();
                yield break;
            }

            PlayerPrefs.SetString(gameVersionKey,Convert.ToBase64String(versionBytes));
        }

        public IEnumerator UpgradeVersionAssetBundles(string url,System.Action onFailed, int timeout = 15, int retryTimes = 5)
        {
            var cachedDir = PathHelper.getAssetBundleSavePath(string.Empty, false, false);
            if (!System.IO.Directory.Exists(cachedDir))
            {
                Logger.Log($"[UpgradeVersionAssetBundles]:创建目录:{cachedDir}");
                System.IO.Directory.CreateDirectory(cachedDir);
            }

            bool succeed = true;
            yield return DownloadAssetBundles(url, abInfos, ()=>
            {
                succeed = false;
            }, timeout, retryTimes);

            if(!succeed)
            {
                onFailed?.Invoke();
                yield break;
            }

            var files = System.IO.Directory.GetFiles(cachedDir, "*.u3d");
            for (int i = 0; i < files.Length; ++i)
            {
                var filePath = files[i];
                var fileName = System.IO.Path.GetFileName(filePath);
                if (!abInfoDic.ContainsKey(fileName))
                {
                    Model.Logger.Log($"[删除文件]:{fileName}无效的文件");
                    System.IO.File.Delete(filePath);
                }
            }

            PlayerPrefs.SetString(gameVersionKey, Convert.ToBase64String(versionBytes));
        }

        public IEnumerator DownloadAssetBundles(string url, ABInfo[] abInfos, System.Action onFailed, int timeout = 15,int retryTimes = 5)
        {
            bool succeed = true;
            for(int i = 0; i < abInfos.Length && succeed;++i)
            {
                int times = retryTimes;

                while(true)
                {
                    yield return DownloadAssetBundle(url, abInfos[i], () =>
                    {
                        succeed = false;
                    }, timeout);

                    if (!succeed && --times >= 0)
                    {
                        succeed = true;
                        Logger.Log($"[DownLoadAssetBundle]:重新下载:[剩余{times}次]");
                    }
                    else
                    {
                        break;
                    }
                }
            }

            if(!succeed)
            {
                onFailed?.Invoke();
            }
        }

        protected IEnumerator DownloadAssetBundle(string url, ABInfo abInfo,System.Action onFailed, int timeout = 15)
        {
            var cachedDir = PathHelper.getAssetBundleSavePath(string.Empty, false, false);
            if (!System.IO.Directory.Exists(cachedDir))
            {
                Logger.Log($"[DownLoadAssetBundle]:创建目录:{cachedDir}");
                System.IO.Directory.CreateDirectory(cachedDir);
            }

            var cachedPath = System.IO.Path.Combine(cachedDir, abInfo.bundleName);
            if (System.IO.File.Exists(cachedPath))
            {
                var md5 = cachedPath.FileMD5();
                if(!string.IsNullOrEmpty(md5) && string.Equals(md5,abInfo.md5))
                {
                    Logger.Log($"[DownLoadAssetBundle]:文件{abInfo.bundleName}已经存在且MD5相同");
                    yield break;
                }
                Model.Logger.Log($"[删除文件]:{abInfo.bundleName}MD5不相同");
                System.IO.File.Delete(cachedPath);
            }

            var targetUrl = System.IO.Path.Combine(url, abInfo.bundleName);

            using (var uwr = UnityWebRequest.Get(targetUrl))
            {
                uwr.timeout = timeout;
                yield return uwr.SendWebRequest();

                if (!uwr.isDone || uwr.isHttpError || uwr.isNetworkError)
                {
                    Logger.LogError($"[DownLoadAssetBundle]:[{targetUrl}]:[reason:{uwr.error}]");
                    onFailed?.Invoke();
                    yield break;
                }

                var md5 = uwr.downloadHandler.data.FileMD5();
                if (string.IsNullOrEmpty(md5) || !string.Equals(md5, abInfo.md5))
                {
                    Logger.LogError($"[DownLoadAssetBundle]:[{targetUrl}]:[reason:verify md5 failed]:[downloaded:{md5} != needed:{abInfo.md5}]");
                    System.IO.File.Delete(cachedPath);

                    onFailed?.Invoke();
                    yield break;
                }

                System.IO.File.WriteAllBytes(cachedPath, uwr.downloadHandler.data);
                Model.Logger.Log($"<color=#00ff00>[DownLoadAssetBundle]:save {abInfo.bundleName} to {cachedPath} succeed</color>");
            }
        }

        public override void Dispose()
        {
            if (Disposed)
            {
                return;
            }

            abInfos = null;
            abInfoDic.Clear();
            abInfoDic = null;

            base.Dispose();
        }
    }
}